<template>
  <div class="tabList">
    <el-form
      ref="registerForm"
      :model="searchForm"
      :inline="true"
      :size="option.size || 'small'"
      :label-width="option.labelWidth || '80px'"
    >
      <template v-for="item in searchColumn">
        <el-form-item :label="item.label" :key="item.prop">
          <slot :name="item.prop + 'Search'" v-bind:row="item">
            <component
              :is="item.type || 'Input'"
              v-bind="item"
              :value.sync="searchForm[item.prop]"
            ></component>
          </slot>
        </el-form-item>
      </template>
      <template v-if="searchColumn.length">
        <el-form-item style="margin-left: 30px">
          <el-button type="primary" icon="el-icon-search" @click="searchChange"
            >搜索</el-button
          >
          <el-button icon="el-icon-delete" @click="searchReset">清空</el-button>
        </el-form-item>
      </template>
    </el-form>
    <!-- 添加按钮行 -->
    <ComMenuTop
      :option="option"
      :selectedRecord="selectedRecord"
      :choiceData="selectionList"
      :allData="data"
      @row-save="rowSave"
      @batch-delete="batchDelete"
      @refresh-list="refreshList"
      @empty-event="emptyEvent"
    >
      <!-- 如果需要传递的slot不固定或者较多，我们可以通过动态插槽名称透传 -->
      <template #[slotName] v-for="(slot, slotName) in $slots">
        <slot :name="slotName" />
      </template>
    </ComMenuTop>
    <!-- 表格 -->
    <el-table
      ref="table"
      v-bind="option"
      :data="data"
      v-loading="loading"
      :size="option.size || 'small'"
      @selection-change="selectionChange"
      style="width: 100%"
    >
      <!-- 多选列 -->
      <el-table-column
        type="selection"
        width="50"
        v-if="isShow(option.selection)"
      >
      </el-table-column>
      <!-- 序号列 -->
      <el-table-column
        type="index"
        label="#"
        width="50"
        v-if="isShow(option.index)"
      ></el-table-column>
      <!-- 数据列 -->
      <el-table-column
        v-for="(item, index) in option.column"
        :key="item.prop"
        v-bind="item"
        :show-overflow-tooltip="isShow(item.overHidden)"
      >
        <template slot="header" slot-scope="{ column }">
          <!-- 自定义表头 -->
          <div :style="{ textAlign: option.headerAlign || 'left' }">
            <slot :name="item.prop + 'Header'" :column="column">
              {{ column.label }}
            </slot>
          </div>
        </template>
        <!-- 自定义列 -->
        <template slot-scope="scope">
          <div :style="{ textAlign: option.align || 'left' }">
            <slot :name="item.prop" :row="scope.row">
              <ComTableName :row="scope.row" :column="option.column[index]" />
            </slot>
          </div>
        </template>
      </el-table-column>
      <!-- 操作列 -->
      <el-table-column
        label="操作"
        v-if="isShow(option.operation)"
        :width="option.operationWidth || '180px'"
      >
        <template slot-scope="scope">
          <el-button
            v-if="isShow(option.viewBtn)"
            :icon="option.viewBtnIcon || 'el-icon-view'"
            type="text"
            @click="beforeOpen(scope.row, scope.$index, 'view')"
            >{{ option.viewBtnText || "查看" }}</el-button
          >
          <el-button
            v-if="isShow(option.editBtn)"
            :icon="option.editBtnIcon || 'el-icon-edit'"
            type="text"
            @click="beforeOpen(scope.row, scope.$index, 'edit')"
            >{{ option.editBtnText || "编辑" }}</el-button
          >
          <el-button
            v-if="isShow(option.delBtn)"
            :icon="option.delBtnIcon || 'el-icon-delete'"
            type="text"
            @click="rowDel(scope.row, 'del')"
            >{{ option.delBtnText || "删除" }}</el-button
          >
          <!-- 自定义操作栏 -->
          <slot name="menu" :row="scope.row"></slot>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页 -->
    <ComPagination
      v-if="isShow(option.pagination)"
      ref="comPagination"
      :page="tablePage"
      @size-change="sizeChange"
      @current-change="currentChange"
    />
    <!-- 弹窗 -->
    <ComDialog
      ref="comDialog"
      :option="dialogOption"
      :visible.sync="dialogOption.visible"
      @on-close="closeDialogHandler"
      @on-submit="onSubmit"
    >
      <!-- 查看 -->
      <template v-if="dialogOption.type === 'view'">
        <ComDescription :list="descriptionList" />
      </template>
      <!-- 编辑 || 添加 -->
      <template v-else>
        <el-form
          :validate-on-rule-change="false"
          :model="addForm"
          :rules="rules"
          :inline="true"
          :size="option.size || 'small'"
          ref="ruleForm"
          :label-width="option.labelWidth || '80px'"
          :style="styleS"
        >
          <el-row>
            <el-col
              v-for="item in formColumn"
              :key="item.label"
              :xs="24"
              :sm="24"
              :lg="item.span ? item.span : 12"
            >
              <el-form-item
                :label="item.label"
                :prop="item.prop"
                class="form-item-box"
              >
                <slot :name="item.prop + 'Form'" v-bind:row="item">
                  <component
                    :is="item.type || 'Input'"
                    v-bind="item"
                    :value.sync="addForm[item.prop]"
                    style="width: 100%"
                  ></component>
                </slot>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </template>
    </ComDialog>
  </div>
</template>

<script>
import ComPagination from "./crud-item/com-pagination.vue";
import ComTableName from "./crud-item/com-table-name.vue";
import ComDescription from "./crud-item/com-descriptions.vue";
import ComMenuTop from "./crud-item/com-menu-top.vue";
import ComDialog from "./crud-item/com-dialog.vue";
import CompFormItem from "./crud-item/formItem";

const dialog = {
  type: "", // 弹窗类型
  visible: false, // 弹窗显示
};

export default {
  name: "ComTable",
  components: {
    ComTableName,
    ComDescription,
    ComPagination,
    ComMenuTop,
    ComDialog,
    CompFormItem,
    ...CompFormItem.components,
  },
  props: {
    form: {
      type: Object,
      default: () => {},
    },
    query: {
      type: Object,
      default: () => {},
    },
    loading: {
      type: Boolean,
      required: true,
      default: () => true,
    },
    option: {
      type: Object,
      required: true,
      default: () => {},
    },
    data: {
      type: Array,
      required: true,
      default: () => [],
    },
    page: {
      type: Object,
      default: () => {},
    },
  },
  data() {
    return {
      // 分页
      tablePage: {
        pageSize: 10,
        currentPage: 1,
        pageSizes: [10, 20, 30, 50],
        total: 0,
      },
      searchForm: {}, // 搜索表单内容
      addForm: {}, // 添加 || 编辑 表单内容
      searchColumn: [], // 需要添加搜索的表格
      formColumn: [], // 需要添加 编辑 || 新增 的表格
      descriptionList: [], // 需要 查看的数据
      selectedRecord: 0, // 批量操作数量
      selectionList: [], // 批量数据
      rowIndex: null, // 操作数据下标

      // 弹窗配置
      dialogOption: Object.assign({}, dialog),
      // 事件命名
      smitS: {
        add: "row-save",
        edit: "row-update",
      },
    };
  },
  watch: {
    page: {
      handler: function (newVal) {
        this.tablePage = Object.assign(this.tablePage, newVal);
      },
      immediate: true,
      deep: true,
    },
    query: {
      handler: function (newVal) {
        if (!Object.keys(newVal).length) return;
        this.searchForm = Object.assign(this.searchForm, newVal);
      },
      deep: true,
    },
    form: {
      handler: function (newVal) {
        if (!Object.keys(newVal).length) return;
        this.addForm = Object.assign({}, newVal);
        if (this.dialogOption.type === "view") {
          this.descriptionList = this.option.column.map((item) => {
            item["val"] = newVal[item.prop];
            return item;
          });
        }
      },
      deep: true,
    },

    "dialogOption.type": function (type) {
      // 判断 form 对应数据是否需要编辑
      this.formColumn = this.option.column.filter((item) =>
        this.isShow(item[type + "Display"])
      );

      if (type === "add") {
        this.addForm = Object.assign({}, this.setForm(this.formColumn));
      }
    },
  },
  computed: {
    /** 解析表单的正则验证***/
    rules() {
      let rules = this.formColumn.reduce((map, i) => {
        if (i.rules) {
          if (!i.disabled) {
            map[i.prop] = i.rules;
          }
        }
        return map;
      }, {});
      return rules;
    },
    styleS() {
      return {
        "--input-width": this.option.labelWidth || "80px",
      };
    },
  },
  created() {
    this.addForm = this.form;
    this.dialogOption.width = this.option.width;
    this.refreshList();
    this.initSearch();
  },
  mounted() {},
  methods: {
    initSearch() {
      this.searchColumn = this.option.column.filter((item) => {
        if (item.search) {
          delete item.value;
          return item;
        }
      });
      let data = this.setForm(this.searchColumn);
      this.searchForm = Object.assign({}, data);
    },

    // 设置 form 默认值
    setForm(data) {
      return data.reduce((a, b) => {
        // form 没有对应的值则查看是否设置 value 默认值
        if (b.value != undefined) {
          a[b.prop] = b.value;
        } else if (b.type == "Checkbox") {
          // 部分组件需要设置默认值为数组
          a[b.prop] = [];
        } else {
          a[b.prop] = "";
        }
        return a;
      }, {});
    },

    // 重置表单
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },

    // 是否显示
    isShow(show) {
      return show === false ? false : true;
    },

    /** 关闭dialog */
    closeDialogHandler() {
      this.$parent.form = {};
      this.addForm = Object.assign({});
      this.dialogOption = Object.assign({}, dialog);
    },

    /** 操作 */
    // 添加
    rowSave() {
      this.open("add");
    },

    // 编辑或查看时需要请求到详情数据
    beforeOpen(row, index, type) {
      this.rowIndex = index;
      this.$emit("before-open", row, type, () => {
        this.open(type);
      });
    },

    // 打开弹窗
    open(type) {
      this.dialogOption.type = type;
      this.dialogOption.visible = true;
    },

    // 弹窗点击提交 done:停止加载动画并关闭弹窗 loading:停止加载动画
    onSubmit(done, loading) {
      this.$refs["ruleForm"].validate((valid) => {
        if (valid) {
          this.$emit(
            this.smitS[this.dialogOption.type],
            this.addForm,
            () => {
              done();
              this.closeDialogHandler();
            },
            () => {
              loading();
            },
            this.rowIndex
          );
        } else {
          loading();
          console.log("error submit!!");
          return false;
        }
      });
    },

    // 删除
    rowDel(row) {
      this.$emit("row-del", row);
    },

    /** 批量操作触发 */
    selectionChange(e) {
      this.selectionList = e;
      this.selectedRecord = e.length;
      this.$emit("selection-change", e);
    },

    /** 清空批量选中数据 */
    emptyEvent() {
      this.selectedRecord = 0;
      this.$refs["table"].clearSelection();
    },

    /** 批量操作 */
    batchDelete() {
      if (this.selectedRecord == 0 || this.selectedRecord.length == 0) {
        this.$message({
          message: "请至少选择一条数据!!",
          type: "warning",
        });
        return false;
      }
      this.$emit("batch-delete");
    },

    /** 每页显示条数 */
    sizeChange(pageSize) {
      this.tablePage.pageSize = pageSize;
      this.refreshList();
      this.$emit("size-change", pageSize);
    },

    /** 当前的页数 */
    currentChange(currentPage) {
      this.tablePage.currentPage = currentPage;
      this.refreshList();
      this.$emit("current-change", currentPage);
    },

    // 清空搜索内容
    searchReset() {
      this.initSearch();
      this.$emit("search-reset");
    },

    searchChange() {
      this.$emit("search-change", this.searchForm);
    },

    /** 刷新表格数据 */
    refreshList() {
      let page = {
        currentPage: this.tablePage.currentPage,
        pageSize: this.tablePage.pageSize,
      };
      this.$emit("on-load", page, this.searchForm);
    },
  },
};
</script>

<style scoped lang="less">
.tabList {
  width: 100%;
  height: 100%;
}

.form-item-box {
  width: 100%;
  display: flex;
  /deep/.el-form-item__content {
    display: block;
    width: calc(100% - var(--input-width));
  }

  /deep/.el-select,
  /deep/.el-date-editor {
    width: 100% !important;
  }
}
</style>
